﻿#pragma once

#include "Math/Math.h"
#include "Misc/StdHead.h"

#include "Containers/Containers.h"
#include "Containers/ThreadSafePtr.h"
#include "Containers/Tuple.h"

#include "WidgetAttribute.h"

#include "Object/Object.h"
#include "Object/NewObject.h"

#include "Widgets/WidgetAnchor.h"

#include "Misc/Tags.h"
#include "Widget.m.h"

class CWidgetTree;
class RApplication;
class RWindow;
struct SRenderWidgetBase;

RENUM()
enum class EWidgetOriginType
{
    LeftTop,
    LeftCenter,
    LeftBottom,

    RightTop,
    RightBottom,
    RightCenter,

    TopCenter,
    BottomCenter,
    Center,
};

RCLASS()
class WHENGINE_API RWidget :public RObject
{
    ROBJECT
public:   

    RWidget();

    virtual ~RWidget(){}
    
    //debug name
    RFIELD()
    TWidgetAttribute<CString> Name=CString(U"NoName");

    TSharedObjectPtr<RWidget> FindSubWidgetByName(const CString& InName);

    //relative position
    RFIELD()
    TWidgetAttribute<float> X=.0f;

    RFIELD()
    TWidgetAttribute<float> Y=.0f; 

    //widget size donot have default value
    //is forget set size,  the size will determined by content
    RFIELD()    
    TWidgetAttribute<float> Width;
    RFIELD()
    TWidgetAttribute<float> Height;

    //控件及子控件的可见性
    RFIELD()
    TWidgetAttribute<bool> Visible=true;

    RFIELD()
    TWidgetAttribute<SVec2> Scale=SVec2(1,1);

    //center of scale, maybe rotation in the future
    RFIELD()
    TWidgetAttribute<EWidgetOriginType> Origin=EWidgetOriginType::LeftTop;

    //invoke PaintSelf() PaintSelfDebugBox() and PaintSubWidgets()
    //@InOutZOrder , is widget paint order, if the widget really paint,should increase it
    //other wise, do not change it.
    // InOutZOrder is a uint16_t , just because render depth is a  16 bit value
    void Paint(uint16_t& InOutZOrder);

    virtual void PaintSelf(uint16_t&  InZOrder,const SRect& InClipArea);

    //choose which sub widget should be painted
    //default is paint all sub widgets
    virtual void PaintSubWidgets(uint16_t&  InZOrder);

    static bool IsPaintDebugBoxEnabled();
    static void EnablePaintDebugBox();
    static void DisablePaintDebugBox();

    void PaintSelfDebugBox();
    virtual SColor GetDebugBoxColor() { return SColor(0,1,0,1); };

    RWidgetWeakPtr GetParent()
    {
        return Parent;
    }
protected:
    RWidget* Parent=nullptr;  
public:
    SVec2 GetAccumulatedScale();

    RFUNCTION()
    virtual void AddSubWidget( RWidgetPtr InWidget);

    RFUNCTION()
    virtual void InsertSubWidget(int64_t Index,RWidgetPtr InWidget);

    RFUNCTION()
    virtual void MoveSubWidget(int64_t FromIndex,int64_t ToIndex);

    RFUNCTION()
    void ClearSubWidgets();
    //clear all sub widgets and destroy them
    void DestroySubWidgets();

    RFUNCTION()
    int64_t GetSubWidgetNum();
    
    RFUNCTION()
    void RemoveSubWidget(RWidgetPtr InWidget);
    
    RFUNCTION()
    void RemoveSubWidgetAt(int64_t Index);

    RFUNCTION()
    void RemoveFromParent();

    void SetParent(RWidget* InParentWidget);

    RFUNCTION()
    TVector<RWidgetPtr> GetSubWidgets();

    RFUNCTION()
    RWidgetPtr GetSubWidget(int64_t Index);

    RFUNCTION()
    bool HasAnySubWidget();
     
    bool IsDescendantsOf(RWidgetPtr InParentWidget);
protected:
    RFIELD()
    TVector<RWidgetPtr> SubWidgets;

public:
    void PaintRenderWidget(std::shared_ptr<SRenderWidgetBase> InRenderWidget);
protected:

public:
    virtual CWidgetTree * GetWidgetTree() const;
    virtual void Register(CWidgetTree * InTree); 
    virtual void Unregister();
    bool IsRegistered();  // is the widget tree know this widget

    //calculate the width and position.X  of widget
    TTuple<float,float> CalculateXAndWidth();
    //calculate the height and position.Y  of widget
    TTuple<float,float> CalculateYAndHeight();

protected:
    CWidgetTree * Tree=nullptr;

    //try to get a widget size when constriants is not enough
    //for example, if the widget only have a top constraint,
    //the widget will try to get Height by CalculateContentHeight()
    virtual float CalculateContentWidth() {return 100;}
    virtual float CalculateContentHeight() {return 100;}

public:
    virtual void Tick(float DeltaSeconds);
    virtual void CalculateSelfPresentPositionAndSize();
    virtual void CalculatePresentPositionAndSize();
    virtual void CalculateChildrenPresentPositionAndSize();
    
    SVec2 GetPresentSize()
    {
        return PresentSize;
    }

    SVec2 GetPresentAbsolutePosition()
    {
        return PresentAbsPosition;
    }   

    //final position and size in window client(do not cosider windows boarder) space
    SVec2 PresentSize=SVec2(0,0);
    SVec2 PresentAbsPosition=SVec2(0,0);

    SVec2 ToLocalPosition(SVec2 InWindowSpacePosition);

    //last frame children's bounding box's width
    float CalculateChildrenBoundingBoxWidth();

    //last frame children's bounding box's height
    float CalculateChildrenBoundingBoxHeight();
public:
    RFIELD()
    SWidgetAnchor Anchor;

public:
    //if true, the sub widget will be clipped by parent widget PaintArea
    TWidgetAttribute<bool> bClipSubWidgets=false;

    //get Clip area  in Window Space
    //all widget is cliped (at least clipped by root widget)
    virtual TOptional<SRect> GetPaintArea();
    virtual TOptional<SRect> GetSubWidgetPaintArea();
public:

    virtual RApplication * GetApplication();

    RWindow* GetWindow();

protected:
    enum class ERenderState
    {
        Normal,          //该控件正常显示
        Invisible,       //该控件由于Visible设置为false,不能显示
        ParentInvisible, //该控件的父/祖父控件Visible设置为false,导致本控件不能显示
        ClippedOut,      //该控件被裁剪掉
    };

    ERenderState RenderState=ERenderState::Normal;
    
    //递归设置子控件的渲染状态
    void RecursivelySetSubwidgetRenderState(ERenderState InState)
    {
        for(auto SubWidget:SubWidgets)
        {
            SubWidget->RenderState=InState;
            SubWidget->RecursivelySetSubwidgetRenderState(InState);
        }
    }
public:
    std::unique_ptr<CTags> Tags;

    bool IsMouseIn(const SIntPoint& MousePosition);
    bool IsCurrentMouseIn();
    SVec2 GetMousePositionInWidget();
    SRect GetRectInScreenSpace();
};


#include <source_location>

template<typename WidgetType,typename ParentWidgetType>
TSharedObjectPtr<WidgetType> NewWidget(ParentWidgetType* InParentWidget
#ifdef USING_SOURCE_LOCATION
    ,const std::source_location & Location= std::source_location::current()
#endif
)
{
    if(InParentWidget)
    {
        auto Widget=NewObject<WidgetType>(InParentWidget
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif    
        );
        InParentWidget->AddSubWidget(Widget);

        return Widget;
    }
    else
    {
        auto Widget=NewObject<WidgetType>(
            WidgetType::StaticClass()
            ,nullptr
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif          
            );
        return Widget;
    }
};

template<typename WidgetType,typename ParentWidgetType>
TSharedObjectPtr<WidgetType> NewWidget(TSharedObjectPtr<ParentWidgetType> InParentWidget
#ifdef USING_SOURCE_LOCATION
    ,const std::source_location & Location= std::source_location::current()
#endif
)
{
    if(InParentWidget)
    {
        auto Widget=NewObject<WidgetType>(InParentWidget
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif    
        );
        InParentWidget->AddSubWidget(Widget);
        return Widget;
    }
    else
    {
        auto Widget=NewObject<WidgetType>(
            WidgetType::StaticClass()
            ,nullptr
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif          
            );
        return Widget;
    }

};

//weak ptr version
template<typename WidgetType,typename ParentWidgetType>
TSharedObjectPtr<WidgetType> NewWidget(TWeakObjectPtr<ParentWidgetType> InParentWidget
#ifdef USING_SOURCE_LOCATION
    ,const std::source_location & Location= std::source_location::current()
#endif
)
{
    if(InParentWidget)
    {
        auto Widget=NewObject<WidgetType>(InParentWidget
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif    
        );
        InParentWidget->AddSubWidget(Widget);
        return Widget;
    }
    else
    {
        auto Widget=NewObject<WidgetType>(
            WidgetType::StaticClass()
            ,nullptr
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif          
            );
        return Widget;
    }

};


template<typename WidgetType = RWidget>
TSharedObjectPtr<WidgetType> NewWidget(CClass * InClass ,RWidget* InParentWidget
#ifdef USING_SOURCE_LOCATION
    ,const std::source_location & Location= std::source_location::current()
#endif
)
{
    if(InParentWidget)
    {
        auto Widget=NewObject<WidgetType>(InClass,InParentWidget
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif    
        );
        InParentWidget->AddSubWidget(Widget);

        return Widget;
    }
    else
    {
        auto Widget=NewObject<WidgetType>(InClass,nullptr
    #ifdef USING_SOURCE_LOCATION    
            ,Location
    #endif    
        );        

        return Widget;
    }
};





template<typename WidgetType = RWidget,typename ParentWidgetType>
TSharedObjectPtr<WidgetType> NewWidget(CClass * InClass ,TSharedObjectPtr<ParentWidgetType> InParentWidget
#ifdef USING_SOURCE_LOCATION
    ,const std::source_location & Location= std::source_location::current()
#endif
)
{
    return NewWidget<WidgetType>(InClass,InParentWidget.Get()
#ifdef USING_SOURCE_LOCATION    
        ,Location
#endif    
    );
};